﻿
using System;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Collections.Specialized;
using System.Net;
using System.IO;

namespace Devfw.OpenAuth
{
    /// <summary>
    /// OpenAuth core class
    /// all SDK use this class get token and access protected resource
    /// </summary>
    public class OpenAuthCore
    {
        #region Construct
        public OpenAuthCore( string oauth_consumer_key, string oauth_consumer_secret )
            : this( oauth_consumer_key, oauth_consumer_secret, OAUTH_VERSION, SignatureType.HMACSHA1, AuthorizationType.HttpAuthorizationHeader )
        {
        }

        public OpenAuthCore( string oauth_consumer_key, string oauth_consumer_secret, string oauth_version, SignatureType oauth_signature, AuthorizationType authorizationType )
        {
            OAuthConsumerKey = oauth_consumer_key;
            OAuthConsumerSecret = oauth_consumer_secret;
            OAuthVersion = oauth_version;
            OAuthSignature = oauth_signature;
            AuthorizationType = authorizationType;
        }
        #endregion

        #region Const & Property
        protected const string OAUTH_VERSION = "1.0";
        protected const string OAUTH_PARAMETER_PREFIX = "oauth_";

        public string OAuthConsumerKey
        {
            get;
            set;
        }

        public string OAuthConsumerSecret
        {
            get;
            set;
        }

        public string OAuthVersion
        {
            get;
            set;
        }

        public SignatureType OAuthSignature
        {
            get;
            set;
        }

        public AuthorizationType AuthorizationType
        {
            get;set;
        }

        private string _contentType;
        public string ContentType
        {
            get
            {
                if (string.IsNullOrEmpty(_contentType))
                    _contentType = "application/x-www-form-urlencoded";

                return _contentType;
            }
            set
            {
                _contentType = value;
            }
        }

        private Encoding _contentEncoding;
        public Encoding ContentEncoding
        {
            get
            {
                if (_contentEncoding == null)
                    _contentEncoding = Encoding.GetEncoding("iso-8859-1");

                return _contentEncoding;
            }
            set
            {
                _contentEncoding = value;
            }
        }
        #endregion

        #region OpenAuth interface
        public RequestTokenResponse request_token( string request_token_url )
        {
            string response = OpenAuthWebRequest( HttpWebMethod.POST, request_token_url, null );

            return new RequestTokenResponse( response );
        }

        public RequestTokenResponse request_token( string request_token_url, string oauth_callback )
        {
            string response = OpenAuthWebRequest(HttpWebMethod.GET, request_token_url,  new ParameterCollection()
            {
                new Parameter("oauth_callback",oauth_callback, ParameterType.Get )
            } );

            return new RequestTokenResponse( response );
        }

        public RequestTokenResponse request_token(string request_token_url, string oauth_callback ,ParameterCollection parameters)
        {
            if (parameters == null)
                parameters = new ParameterCollection();

            parameters.Add(new Parameter("oauth_callback", oauth_callback, ParameterType.Get));

            string response = OpenAuthWebRequest(HttpWebMethod.GET, request_token_url, parameters);
            return new RequestTokenResponse(response);
        }


        public void authorize( string authorize_url, string oauth_token, string oauth_callback )
        {
            string url = generate_authorize_url( authorize_url, oauth_token, oauth_callback );
            HttpContext.Current.Response.Redirect( url );
        }

    
        public string generate_authorize_url( string authorize_url, string oauth_token, string oauth_callback )
        {
            return OpenAuthHelper.CombinUrl(authorize_url, string.Format("oauth_token={0}&oauth_callback={1}", oauth_token, OpenAuthHelper.ParameterEncode(oauth_callback)));
        }

        public AccessTokenResponse access_token( string access_token_url, string oauth_token, string oauth_token_screct, string oauth_verifier )
        {
            string response = OpenAuthWebRequest( HttpWebMethod.GET, access_token_url, oauth_token, oauth_token_screct, new ParameterCollection()
            {
                new Parameter( "oauth_verifier", oauth_verifier, ParameterType.Get )
            }, this.AuthorizationType );

            return new AccessTokenResponse( response );
        }


        public AccessTokenResponse access_token(string access_token_url, string oauth_token, string oauth_token_screct, ParameterCollection parameters)
        {
            string response = OpenAuthWebRequest(HttpWebMethod.GET, access_token_url, oauth_token, oauth_token_screct, parameters, this.AuthorizationType);

            return new AccessTokenResponse(response);
        }
        #endregion

        #region OpenAuth WebRequest
        public string OpenAuthWebRequest( HttpWebMethod method, string url )
        {
            return OpenAuthWebRequest( method, url, null );
        }

        public string OpenAuthWebRequest( HttpWebMethod method, string url, ParameterCollection parameters )
        {
            return OpenAuthWebRequest( method, url, "", "", parameters, this.AuthorizationType );
        }

        public string OpenAuthWebRequest( HttpWebMethod method, string url, string oauth_token, string oauth_token_secret )
        {
            return OpenAuthWebRequest( method, url, oauth_token, oauth_token_secret, null, this.AuthorizationType );
        }

        public string OpenAuthWebRequest( HttpWebMethod method, string url, string oauth_token, string oauth_token_secret, ParameterCollection parameters )
        {
            return OpenAuthWebRequest( method, url, oauth_token, oauth_token_secret, parameters, this.AuthorizationType );
        }

        public string OpenAuthWebRequest(HttpWebMethod method, string url, string oauth_token, string oauth_token_secret,
            ParameterCollection parameters, AuthorizationType authorizationType)
        {
            return OpenAuthWebRequest(method, url, oauth_token, oauth_token_secret, parameters, this.AuthorizationType,ContentType , ContentEncoding);
        }


        public string OpenAuthWebRequest( HttpWebMethod method, string url, string oauth_token, string oauth_token_secret,
            ParameterCollection parameters, AuthorizationType authorizationType , string contentType, Encoding encoding)
        {
            string absoluteUri = OpenAuthHelper.ConstructRequestUrl( url );

            var signatureParameters = OpenAuthHelper.Signature( method.ToString(), absoluteUri, parameters, this.OAuthConsumerKey, this.OAuthConsumerSecret,
                oauth_token, oauth_token_secret, OpenAuthHelper.GenerateTimeStamp(), OpenAuthHelper.GenerateNonce(), this.OAuthVersion,
                this.OAuthSignature );

            return OpenAuthWebRequest(method, url, signatureParameters, authorizationType, contentType, encoding);
        }

        public static string OpenAuthWebRequest( HttpWebMethod method, string absoluteUri, ParameterCollection parameters, OpenAuth.AuthorizationType authorizationType,
            string contentType , Encoding encoding)
        {
            string fullUrl = absoluteUri;

            string authorizationHeader = string.Empty;
            string queryString = string.Empty;
            string requestDataBody = string.Empty;

            // 处理OAuth参数
            switch( authorizationType )
            {
                case AuthorizationType.UrlQuery:
                    queryString = OpenAuthHelper.ToQueryString( parameters.GetParameters( ParameterType.OAuth ) ); //OpenAuthHelper.CombinUrl( absoluteUri, dummy );
                    break;

                case AuthorizationType.HttpAuthorizationHeader:
                    StringBuilder sbHeader = new StringBuilder();

                    sbHeader.Append( "OAuth" );
                    var oauthParameters = parameters.GetParameters( ParameterType.OAuth );
                    foreach( var p in oauthParameters )
                    {
                        sbHeader.AppendFormat( " {0}=\"{1}\",", p.Name, OpenAuthHelper.ParameterEncode( p.Value ) );
                    }
                    sbHeader.Remove( sbHeader.Length - 1, 1 );
                    authorizationHeader = sbHeader.ToString();
                    break;

                case AuthorizationType.PostRequestBody:
                    requestDataBody = OpenAuthHelper.ToPostBody(parameters.GetParameters(ParameterType.OAuth));
                    break;
                default:
                    throw new ArgumentException( "Unknown authorization type", "authorizationType" );
            }

            // 合并query
            string qs2 = OpenAuthHelper.ToQueryString( parameters.GetParameters( ParameterType.Get ) );
            queryString = OpenAuthHelper.CombinQueryString( queryString, qs2 );
            fullUrl = OpenAuthHelper.CombinUrl( fullUrl, queryString );

            // 合并post
            string body2 = OpenAuthHelper.ToPostBody( parameters.GetParameters( ParameterType.Post ) );
            requestDataBody = OpenAuthHelper.CombinQueryString(requestDataBody, body2);


            return OpenAuthHelper.WebRequest(method, fullUrl, contentType, encoding, requestDataBody, authorizationHeader);
        }


        public string OpenAuthFormDataPostWebRequest( string url, string oauth_token, string oauth_token_secret, ParameterCollection parameters )
        {
            return OpenAuthFormDataPostWebRequest( url, oauth_token, oauth_token_secret, parameters, this.AuthorizationType );
        }

        public string OpenAuthFormDataPostWebRequest( string url, string oauth_token, string oauth_token_secret,
            ParameterCollection parameters, AuthorizationType authorizationType )
        {
            HttpWebMethod method = HttpWebMethod.POST;
            string absoluteUri = OpenAuthHelper.ConstructRequestUrl( url );

            var signatureParameters = OpenAuthHelper.Signature( method.ToString(), absoluteUri, parameters, this.OAuthConsumerKey, this.OAuthConsumerSecret,
                oauth_token, oauth_token_secret, OpenAuthHelper.GenerateTimeStamp(), OpenAuthHelper.GenerateNonce(), this.OAuthVersion,
                this.OAuthSignature );

            // 生成Post
            StringBuilder buffer = new StringBuilder();

            string boundary = Guid.NewGuid().ToString( "N" );
            MemoryStream ms = new MemoryStream();
            // StreamWriter sw = new StreamWriter( ms, Encoding. );
            // Steram

            string contentType = string.Format( "multipart/form-data; boundary={0}", boundary );

            string header = string.Format( "--{0}", boundary );
            string footer = string.Format( "--{0}--", boundary );

            // var contentEncoding = "iso-8859-1";

            ParameterCollection pcDummy = new ParameterCollection();
            pcDummy.AddRange( signatureParameters.GetParameters( ParameterType.Post ) );
            if( authorizationType == AuthorizationType.FormDataPost )
            {
                pcDummy.AddRange( signatureParameters.GetParameters( ParameterType.OAuth ) );
            }
            pcDummy.Sort();

            foreach( Parameter pTemp in pcDummy )
            {
                PostParameter p = pTemp as PostParameter;

                if( p != null )
                {
                    buffer.AppendLine( header );
                    buffer.Append( String.Format( "Content-Disposition: form-data; name=\"{0}\"", p.Name ) );
                    if( !string.IsNullOrEmpty( p.ExtAtContentDisposition ) )
                    {
                        buffer.Append( "; " + p.ExtAtContentDisposition );
                    }
                    buffer.AppendLine();

                    if( p.IsBinary )
                    {
                        buffer.AppendLine( "Content-Type: application/octet-stream; " );
                        buffer.AppendLine();
                        buffer.AppendLine( p.Value );
                    }
                    else
                    {
                        buffer.AppendLine();
                        buffer.AppendLine( Encoding.GetEncoding( "iso-8859-1" ).GetString( Encoding.UTF8.GetBytes( p.Value ) ) );
                    }
                }
                else
                {
                    buffer.AppendLine( header );
                    buffer.AppendLine( String.Format( "Content-Disposition: form-data; name=\"{0}\"", pTemp.Name ) );
                    buffer.AppendLine();
                    buffer.AppendLine( Encoding.GetEncoding( "iso-8859-1" ).GetString( Encoding.UTF8.GetBytes( pTemp.Value ) ) );
                    // buffer.AppendLine( pTemp.Value );
                }
            }

            buffer.AppendLine( footer );

            return OpenAuthCustomPostWebRequest( url, signatureParameters, contentType, buffer.ToString(), authorizationType );
        
            
            /*
            if( authorizationType == AuthorizationType.FormDataPost )
            {
                foreach( Parameter p in signatureParameters.GetParameters( ParameterType.OAuth ) )
                {
                    buffer.AppendLine( header );
                    buffer.Append( String.Format( "Content-Disposition: form-data; name=\"{0}\";", p.Name ) );
                    /*if( !string.IsNullOrEmpty( p.ExtAtContentDisposition ) )
                    {
                        buffer.Append( p.ExtAtContentDisposition + ";" );
                    }* /
                    //buffer.AppendLine(); // close Content-Disposition

                    /*if( !string.IsNullOrEmpty( p.ContentType ) )
                    {
                        buffer.AppendLine( string.Format( "Content-Type: {0}; charset={1}", p.ContentType, p.Charset ) );
                    }
                    if( !string.IsNullOrEmpty( p.ContentTransferEncoding ) )
                    {
                        buffer.AppendLine( string.Format( "Content-Transfer-Encoding: {0}", p.ContentTransferEncoding ) );
                    }* /
                    buffer.AppendLine();
                    buffer.AppendLine( p.Value );
                }
            }*/

            
        }


        /// <summary>
        /// 发起指定的请求
        /// </summary>
        /// <param name="absoluteUri"></param>
        /// <param name="parameters"></param>
        /// <param name="contentType"></param>
        /// <param name="postBody"></param>
        /// <param name="authorizationType"></param>
        /// <returns></returns>
        public static string OpenAuthCustomPostWebRequest( string absoluteUri, ParameterCollection parameters, string contentType, string postBody, OpenAuth.AuthorizationType authorizationType )
        {
            // HttpWebMethod method 
            HttpWebMethod method = HttpWebMethod.POST;
            string fullUrl = absoluteUri;

            string authorizationHeader = string.Empty;
            string queryString = string.Empty;
            // string postBody = string.Empty;

            // 处理OAuth参数
            switch( authorizationType )
            {
                case AuthorizationType.UrlQuery:
                    queryString = OpenAuthHelper.ToQueryString( parameters.GetParameters( ParameterType.OAuth ) ); //OpenAuthHelper.CombinUrl( absoluteUri, dummy );
                    break;

                case AuthorizationType.HttpAuthorizationHeader:
                    StringBuilder sbHeader = new StringBuilder();

                    sbHeader.Append( "OAuth" );
                    var oauthParameters = parameters.GetParameters( ParameterType.OAuth );
                    foreach( var p in oauthParameters )
                    {
                        sbHeader.AppendFormat( " {0}=\"{1}\",", p.Name, OpenAuthHelper.ParameterEncode( p.Value ) );
                    }
                    sbHeader.Remove( sbHeader.Length - 1, 1 );
                    authorizationHeader = sbHeader.ToString();
                    break;

                case AuthorizationType.PostRequestBody:
                    postBody = OpenAuthHelper.ToPostBody( parameters.GetParameters( ParameterType.OAuth ) );
                    break;
                case AuthorizationType.FormDataPost:
                    break;
                default:
                    throw new ArgumentException( "Unknown authorization type", "authorizationType" );
            }

            // 合并query
            string qs2 = OpenAuthHelper.ToQueryString( parameters.GetParameters( ParameterType.Get ) );
            queryString = OpenAuthHelper.CombinQueryString( queryString, qs2 );
            fullUrl = OpenAuthHelper.CombinUrl( fullUrl, queryString );

            // 合并post
            // string body2 = OpenAuthHelper.ToPostBody( parameters.GetParameters( ParameterType.Post ) );
            // postBody = OpenAuthHelper.CombinQueryString( postBody, body2 );

            return OpenAuthHelper.WebRequest( method, fullUrl, contentType, postBody, authorizationHeader );
        }
        #endregion 
    }
}